package media
{
	/**
	 * @auth Chunning
	 * @purpose MeidaPlayer could play flv...
	 * @date 2009-3-19
	* */	
		
	import flash.display.Bitmap;
	import flash.display.MovieClip;
	import flash.display.StageDisplayState;
	import flash.events.AsyncErrorEvent;
	import flash.events.Event;
	import flash.events.IOErrorEvent;
	import flash.events.MouseEvent;
	import flash.events.NetStatusEvent;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.media.SoundTransform;
	import flash.net.NetConnection;
	import flash.net.NetStream;
	
	[Event(name="get_metadata",type="com.components.media.MediaPlayerEvents")]
	[Event(name="on_cuepoint",type="com.components.media.MediaPlayerEvents")]
	[Event(name="start",type="com.components.media.MediaPlayerEvents")]
	[Event(name="stop",type="com.components.media.MediaPlayerEvents")]

	public class MediaPlayer extends MovieClip
	{
		//START
		
		//PROPERTY
		private var netStream:NetStream;
		
		private var netCon:NetConnection;
		
		private var view:MediaPlayerView;
		
		private var _path:String;
		
		private var _autofit:Boolean;
		
		private var mode:String = MediaPlayerScaleMode.EXTRA_FIT;
		
		private var rate:Number;
		
		private var _mediaInfo:Object;
				
		private var parseStatus:Object;	
		
		private var ctrlPanel:I_PlayerCtrlPanel;	
		
		private var metaData:Object;
		
		private var normalPropety:Object;
								
		//CONSTRUCTOR		
		public function MediaPlayer()
		{
			init();
			
		}
		
		//METHODS
		private function init():void
		{
			
			registerMethods();
						
			view = new MediaPlayerView(this);
			
			view.initPlayerView();
			
			ctrlPanel = this.getChildByName("ctrlPanel") as I_PlayerCtrlPanel;
								
			netCon = new NetConnection();
			
			netCon.connect(null);
											
			netStream = new NetStream(netCon);
			
			updateSoundTransform();
			
			configNSListeners();
			
			netStream.client = this;
					
			netStream.checkPolicyFile = true;
						
			this.view.attachNS(netStream);
			
			configListeners();
																												
		}
		
		//config listeners....
		private function configListeners():void
		{
			
			this.addEventListener(MouseEvent.MOUSE_OVER,overHandler);
			
			this.addEventListener(MouseEvent.MOUSE_OUT,outHandler);
			
			this.addEventListener(MediaPlayerEvents.GET_METADATA,getMetaDataHandler);
			
			this.callCtrlMethod("addEventListener",PlayerCtrlEvents.PLAY,this.playHandler);
			
			this.callCtrlMethod("addEventListener",PlayerCtrlEvents.RESUME,this.resumeHandler);
			
			this.callCtrlMethod("addEventListener",PlayerCtrlEvents.PAUSE,this.pauseHandler);
			
			this.callCtrlMethod("addEventListener",PlayerCtrlEvents.CHANGE_VOLUME,this.chgVolumeHandler);
			
			this.callCtrlMethod("addEventListener",PlayerCtrlEvents.SEEK,this.seekHandler);
			
			this.callCtrlMethod("addEventListener",PlayerCtrlEvents.STOP,videoStopHandler);
			
			this.callCtrlMethod("addEventListener",PlayerCtrlEvents.FULL_SCREEN,fullScreenHandler);
			
			this.callCtrlMethod("addEventListener",PlayerCtrlEvents.NORMAL_VIEW,normalViewHandler);
						
		}
		
		//handlers for ctrl panel...
		
		// video stop handler...
		private function videoStopHandler(evt:PlayerCtrlEvents):void
		{
			
			close();
			
		}
		
				
		// norm view handler...
		private function normalViewHandler(evt:PlayerCtrlEvents):void
		{
			
			stage.removeEventListener(Event.RESIZE,stageResizeHandler);
			
			stage.displayState = StageDisplayState.NORMAL;
			
			view.bgWidth = this.normalPropety.bgWidth;
			
			view.bgHeight = this.normalPropety.bgHeight;
			
			setScaleMode(mode,rate);
			
			x = this.normalPropety.x;
			
			y = this.normalPropety.y;
			
			this.normalPropety.parent.addChild(this);
			
			callCtrlMethod("changeSize",this.normalPropety.bgWidth,this.normalPropety.bgHeight);
					
		}
		
		private function fullScreenHandler(evt:PlayerCtrlEvents):void
		{
			
			stage.displayState = StageDisplayState.FULL_SCREEN;
			
			stage.addEventListener(Event.RESIZE,stageResizeHandler);
			
			normalPropety = new Object;
			
			normalPropety.x = x;
			
			normalPropety.y = y;
			
			normalPropety.parent = parent;
			
			normalPropety.bgHeight = view.bgHeight;
			
			normalPropety.bgWidth = view.bgWidth;
						
			callCtrlMethod("changeSize",stage.stageWidth,stage.stageHeight);
			
			view.bgHeight = stage.stageHeight;
			
			view.bgWidth = stage.stageWidth;
			
			setScaleMode(mode,rate);
			
			x = 0;
			
			y = 0;
			
			stage.addChild(this);
			
		}
		
		//on stage change to normal size...
		private function stageResizeHandler(evt:Event):void
		{
			
			normalViewHandler(null);
						
		}
		
		
		private function playHandler(evt:PlayerCtrlEvents):void
		{
			
			this.playVideo();
			
		}
		
		private function getMetaDataHandler(evt:MediaPlayerEvents):void
		{
			
			metaData = evt.evtInfo;
			
			setCtrlProperty("totalTime",evt.evtInfo["duration"]);			
			
		}
		
		private function seekHandler(evt:PlayerCtrlEvents):void
		{
			
			try{
				
				var offset:Number = Number(evt.ctrlInfo ) * metaData["duration"];
				
				offset = offset < 0 ? 0 : offset;
				
				offset = offset > metaData["duration"] ? metaData["duration"] : offset;
				
				seek(offset);
				
			}catch(err:Error){};	
		}
		
		private function resumeHandler(evt:PlayerCtrlEvents):void
		{
			
			this.resume();
			
		}
		
		private function pauseHandler(evt:PlayerCtrlEvents):void
		{
			
			this.pause();
			
		}
		
		private function chgVolumeHandler(evt:PlayerCtrlEvents):void
		{
			
			this.updateSoundTransform();
			
		} 
		
		//over video...
		private function overHandler(evt:MouseEvent):void
		{
			
			this.callCtrlMethod("overVideo");			
			
		}
		
		//update sound transform...
		private function updateSoundTransform():void
		{
			
			var st:SoundTransform = new SoundTransform;
			
			st.volume = this.getVolume();
			
			this.netStream.soundTransform = st;
									
		}
		
		//out video...
		private function outHandler(evt:MouseEvent):void
		{
			
			var mousePos:Point = new Point(this.mouseX,this.mouseY);
			
			var thisRect:Rectangle = new Rectangle(0,0,this.width,this.height);
						
			if(!thisRect.containsPoint(mousePos))
			{
				
				this.callCtrlMethod("outVideo");
				
			}
		}
		
		
		//set ctrl property...
		private function setCtrlProperty(prop:String,val:Object):void
		{
			
			if(this.ctrlPanel is I_PlayerCtrlPanel)
			{
				
				this.ctrlPanel[prop] = val;
				
			}
			
		}
		
		//call ctrl method...
		private function callCtrlMethod(name:String,...para):*
		{
			
			if(this.ctrlPanel is I_PlayerCtrlPanel)
			{
				
				return this.ctrlPanel[name].apply(this,para);
				
			}
			
			return null;
			
		}
		
		
		//config net stream listeners...
		private function configNSListeners():void
		{
			
			this.netStream.addEventListener(IOErrorEvent.IO_ERROR,nsIOErrorHandler);
			
			this.netStream.addEventListener(AsyncErrorEvent.ASYNC_ERROR,nsAsyncErrorHandler);
			
			this.netStream.addEventListener(NetStatusEvent.NET_STATUS,nsNetStatusHandler);
						
		}
		
		//on netstream io error...
		private function nsIOErrorHandler(evt:IOErrorEvent):void
		{
			this.dispatchEvent(evt);	
		}
				
		//on netstream async error...
		private function nsAsyncErrorHandler(evt:AsyncErrorEvent):void
		{
			
			//do nothing...
						
		}
		
		//on net status error...
		private function nsNetStatusHandler(evt:NetStatusEvent):void
		{
						
			var code:String = evt.info.code;
			
						
			var arrCode:Array = code.split(".");
			
			try			
			{
								
				var exec:Function = this.parseStatus[arrCode[1]][arrCode[2]];
											
				exec();
				
			}catch(err:Error)
			{
				
				trace("Have not Process Status:" + code);
				
				return;				
				
			}
			
			
		}	
		
		//register parse status method....
		private function registerMethods():void
		{
			
			this.parseStatus = {Play:{Start:this.onStart,
									  Stop:this.onStop,
									  StreamNotFound:this.onStreamNotFound,
									  FileStructureInvalid:onFileStructureInvalid},
								Buffer:{Full:this.full,
										Empty:this.empty,
										Flush:this.flush}};
			
		}
		
		//on file structure invalid...
		private function onFileStructureInvalid():void
		{
			//handler error.		
			
		}
		
		//on stream not found...
		private function onStreamNotFound():void
		{
			
			//handler error.
			
		}
		
		//buffer empty...
		private function empty():void
		{
			
			view.showBuffer();
			
			this.setCtrlProperty("status",PlayerStatus.BUFFERING);
			
		}
		
		//buffer full...
		private function full():void
		{
			
			view.hideBuffer();
			
			this.setCtrlProperty("status",PlayerStatus.FULL);
			
		}
		
		//buffer flush...
		private function flush():void
		{
			
			view.hideBuffer();
			
			this.setCtrlProperty("status",PlayerStatus.FLUSH);
			
		}
		
		//start...
		private function onStart():void
		{
									
			this.setCtrlProperty("status",PlayerStatus.PLAYING);
									
			this.dispatchEvent(new MediaPlayerEvents(MediaPlayerEvents.START));
					
		}	
		
		//on stop...
		private function onStop():void
		{
			
			view.hideBuffer();
					
			this.setCtrlProperty("status",PlayerStatus.STOP);
			
			this.dispatchEvent(new MediaPlayerEvents(MediaPlayerEvents.STOP));
						
		}
		
		//on enterframe...
		private function enterFrameHandler(evt:Event):void
		{
						
			this.callCtrlMethod("downloadProgress",this.netStream.bytesLoaded / this.netStream.bytesTotal);
			
			this.callCtrlMethod("playProgress",this.netStream.time / this.mediaInfo["duration"]);
			
			this.setCtrlProperty("playTime",netStream.time);
			
		}
				
		//set meida path...
		public function set path(url:String):void
		{
			
			this._path = url;
			
			view.showBuffer();
					
			view.video.visible = false;
			
			netStream.close();
									
			netStream.play(url);	
														
		}
						
		//get media path...
		public function get path():String
		{
			
			return this._path;
			
		}
		
		
		//get total time...
		public function get totalTime():Number
		{
			
			var duration:Number = this._mediaInfo == null || !this._mediaInfo.hasOwnProperty("duration") ? 0 : this._mediaInfo["duration"];
								
			return duration;
		}
		
		//get played time...
		public function get time():Number
		{
			
			return this.netStream.time;
			
		}
		
		//get total bytes...
		public function get bytesTotal():Number
		{
			
			return this.netStream.bytesTotal;
			
		}
		
		
		//get download bytes...
		public function get bytesLoaded():Number
		{
			
			return this.netStream.bytesLoaded;
			
		}
		
		//get media info...
		public function get mediaInfo():Object
		{
			
			return this._mediaInfo;
			
		}
		
		//set video width...
		public override function set width(value:Number):void
		{
			
			this.view.bgWidth = value;			
			
		}	
		
		//set video height...
		public override function set height(value:Number):void
		{
			
			this.view.bgHeight = value;
			
		}
		
		//screen shoot...
		public function shoot():Bitmap
		{
						
			return null;
			
		}
		
		//set buffer time...
		public function set bufferTime(val:Number):void
		{
			
			this.netStream.bufferTime = val;
			
		}
		
		//get buffer time...
		public function get bufferTime():Number
		{
			
			return this.netStream.bufferTime;
			
		}
		
		
		//set scale mode...
		public function setScaleMode(mode:String,rate:Number = NaN):void
		{
			
			this.mode = mode;
			
			this.rate = rate;

			if(this._mediaInfo == null)
			{
				
				return;
				
			}
			
			var bgWidth:Number = this.view.bgWidth == 0 ? this._mediaInfo["width"] : this.view.bgWidth;
			
			var bgHeight:Number = this.view.bgHeight == 0 ? this._mediaInfo["height"] : this.view.bgHeight;
			
			var result:Object;
												
			switch(this.mode)
			{
				
				case MediaPlayerScaleMode.EXTRA_FIT:
								
				this.view.setVideoSize(bgWidth,bgHeight);
				
				return;
				
				case MediaPlayerScaleMode.KEEP_RATE:
				
				if(!isNaN(rate))
				{
					
					result = this.countRate(bgWidth,bgHeight,rate,false);
					
					bgWidth = result.width;
					
					bgHeight = result.height;
					
				}
				
				this.keepRate(bgWidth,bgHeight,false,rate);
								
				return;
				
				case MediaPlayerScaleMode.NO_BORDER:
				
				if(!isNaN(rate))
				{
					
					result = this.countRate(bgWidth,bgHeight,rate,true);
					
					bgWidth = result.width;
					
					bgHeight = result.height;
					
				}
				
				this.keepRate(bgWidth,bgHeight,true,rate);
				
				return;
				
				case MediaPlayerScaleMode.NO_SCALE:
				
				this.view.setVideoSize(this._mediaInfo["width"],this.mediaInfo["height"]);
				
				return;
								
			}
			
		}
		
		//scale keep rate...
		private function keepRate(width:Number,height:Number,noBorder:Boolean = false,specs:Number = NaN):void
		{
			
			var rateWidth:Number = this._mediaInfo["width"] / width;
			
			var rateHeight:Number = this.mediaInfo["height"] / height;
			
			var rate:Number;
						
			if(noBorder)
			{						
			
				rate = rateWidth < rateHeight ? rateWidth : rateHeight;
			
			}else{
				
				rate = rateWidth > rateHeight ? rateWidth : rateHeight;
				
			}
			
			
			this.view.setVideoSize(this._mediaInfo["width"] / rate,this._mediaInfo["height"] / rate);
			
		}
		
		//count rate...
		private function countRate(width:Number,height:Number,rate:Number,noBorder:Boolean):Object
		{
			
			var result:Object = new Object;
			
			var newWidth:Number = height * rate;
			
			var newHeight:Number = width / rate;
						
			if( (newWidth > width && noBorder ) || (newHeight >= height && !noBorder))
			{
					
				result.height = height;
					
				result.width = newWidth;
					
			}
				
			if((newHeight >= height && noBorder) || (newWidth > width && !noBorder))
			{
					
					result.width = width;
					
					result.height = newHeight;
					
			}
						
			return result;
						
		}
				
		
		//onMetaData...
		public function onMetaData(info:Object):void
		{
			
			this._mediaInfo = info;
			
			this.addEventListener(Event.ENTER_FRAME,enterFrameHandler);		
																
			this.setScaleMode(this.mode,this.rate);
			
			view.hideBuffer();
			
			view.video.visible = true;
			
			this.dispatchEvent(new MediaPlayerEvents(MediaPlayerEvents.GET_METADATA,info));
			
		}
		
		//onCuePoint...
		public function onCuePoint(info:Object):void
		{
			
			this.dispatchEvent(new MediaPlayerEvents(MediaPlayerEvents.ON_CUEPOINT,info));			
			
		}
		
		//play...
		public function playVideo():void
		{
		
			if(this.path is String)
			{
				
				path = path;
								
			}
			
		}
		
		//puase...
		public function pause():void
		{
			/*
			 * test to hide buffering
			*/
			view.hideBuffer();
			
			this.setCtrlProperty("status",PlayerStatus.PAUSE);
			
			this.netStream.pause();
		}
		
		//resume...
		public function resume():void
		{
			
			this.setCtrlProperty("status",PlayerStatus.PLAYING);
			
			this.netStream.resume();
			
		}
		
		//seek...
		public function seek(offset:Number):void
		{
			
			try{			
			
				this.netStream.seek(offset);	
			
			}catch(err:Error){};
			
		}
		
		//close net stream...
		public function close():void
		{
						
			view.video.visible = false;
			
			view.hideBuffer();
					
			this.setCtrlProperty("status",PlayerStatus.STOP);
			
			this.removeEventListener(Event.ENTER_FRAME,enterFrameHandler);
			
			this.netStream.close();
			
			dispatchEvent(new MediaPlayerEvents(MediaPlayerEvents.STOP));
			
		}	
		
		//get volume value...
		public function getVolume():Number
		{
			
			return (this.callCtrlMethod("getVolume") == null ? 1 : this.callCtrlMethod("getVolume"));
			
		}	
		
				
		//END		
	}
}